﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GOFconsole
{
    public class Chapter14_Observer
    {
        public void ObserverTest()
        {
            //模型
            ConcreteSubject cs = new ConcreteSubject();
            cs.Attach(new ConcreteObserver(cs, "X"));
            cs.Attach(new ConcreteObserver(cs, "Y"));
            cs.Attach(new ConcreteObserver(cs, "Z"));

            cs.SubjectState = "ABC";
            cs.Notify();

            //DEMO
            Boss huhansan = new Boss();

            //股票的同事
            StockOb_Demo tongshi1 = new StockOb_Demo("张XX", huhansan);

            huhansan.Attach(tongshi1);

            huhansan.SubjectState = "我胡汉三回来啦！";
            huhansan.Notify();

            //用委托通知
            Boss_delegate huhansi = new Boss_delegate();
            StockOb_Delegate tongshi2 = new StockOb_Delegate("王YY", huhansi);
            //将看股票同事的关闭股票方法挂钩到老板的更新方法上，也就是将不同类的不同方法委托给老板类的更新方法了
            huhansi.Update += new EventHandler(tongshi2.CloseStockMarket);

            huhansi.SubjectState = "老板回来啦！";
            huhansi.Notify();
        }        
    }

    #region 模型
    //Subject类，翻译为主题或抽象通知者，一般用一个抽象类或者一个接口实现。它把所有对观察者对象的引用保存在一个聚集里，
    //每个主题都可以有任何数量的观察者。抽象主题提供一个接口，可以增加和删除观察者对象。
    abstract class Subject_C14
    { 
        private IList<Observer> observers=new List<Observer>();
        //增加观察者
        public void Attach(Observer observer)
        {
            observers.Add(observer);
        }
        //移除观察者
        public void Detach(Observer observer)
        {
            observers.Remove(observer);
        }
        //通知
        public void Notify()
        {
            foreach (Observer o in observers)
            {
                o.Update();
            }
        }
    }
    //Observer类，抽象观察者，为所有的观察者定义一个接口，在得到主题的通知时更新自己。这个接口叫更新接口，通常
    //包含一个Update()方法，这个方法叫做更新方法。
    abstract class Observer
    {
        public abstract void Update();
    }
    //ConcreteSubject类，具体主题类，将有关状态存入具体观察者对象，
    class ConcreteSubject : Subject_C14
    {
        private string subjectState;
        //具体被观察者状态
        public string SubjectState
        {
            get { return subjectState; }
            set { subjectState = value; }
        }
    }
    //ConcreteObserver类，具体观察者，实现抽象观察者角色所要求的更新接口，以便使本身的状态与主题的状态相协调。
    //具体观察者角色可以保存一个指向具体主题对象的引用。
    class ConcreteObserver : Observer
    {
        private string name;
        private string observerState;
        private ConcreteSubject subject;

        public ConcreteObserver(ConcreteSubject subject, string name)
        {
            this.subject = subject;
            this.name = name;
        }
        
        public override void Update()
        {
            observerState = subject.SubjectState;
            Console.WriteLine("观察者{0}的新状态是{1}",name,observerState);
        }

        public ConcreteSubject Subject
        {
            get { return Subject; }
            set { subject = value; }
        }
    }
    #endregion

    #region DEMO(老板通知)
    //通知者接口
    interface Subject_Demo
    {
        void Attach(Observer_Demo ob);
        void Detach(Observer_Demo ob);
        void Notify();
        string SubjectState { get; set; }
    }
    class Boss : Subject_Demo
    { 
        //同事列表
        private IList<Observer_Demo> obs = new List<Observer_Demo>();
        private string action;

        public void Attach(Observer_Demo ob)
        {
            obs.Add(ob);
        }
        public void Detach(Observer_Demo ob)
        {
            obs.Remove(ob);
        }
        public void Notify()
        {
            foreach (var o in obs)
            {
                o.Update();
            }
        }
        //老板状态
        public string SubjectState
        {
            get { return action; }
            set { action = value; }
        }
    }

    abstract class Observer_Demo
    {
        protected string name;
        protected Subject_Demo sub;

        public Observer_Demo(string name,Subject_Demo sub)
        {
            this.name = name;
            this.sub = sub;
        }

        public abstract void Update();
    }
    //看股票的同事
    class StockOb_Demo : Observer_Demo
    {
        public StockOb_Demo(string name, Subject_Demo sub)//不是老板，而是抽象的通知者
            : base(name, sub)
        { }

        public override void Update()
        {
            Console.WriteLine("{0} {1}关闭股票行情，继续工作！",sub.SubjectState,name);//不是老板状态，是抽象通知者状态
        }
    }

    #endregion

    #region 用委托实现(去掉了父类'抽象观察者类'，通知者和观察者之间相互不知道，由客户端来决定通知谁)
    //看股票的同事
    class StockOb_Delegate 
    {
        private string name;
        private Subject_Delegate sub;
        public StockOb_Delegate(string name, Subject_Delegate sub)         
        {
            this.name = name;
            this.sub = sub;
        }

        public void CloseStockMarket()//不再是统一的"更新"方法了，改为独立的"关闭股票"
        {
            Console.WriteLine("{0} {1}关闭股票行情，继续工作！", sub.SubjectState, name);//不是老板状态，是抽象通知者状态
        }
    }
    //抽象通知者不希望依赖抽象观察者，所以增加观察者和减少观察者的方法也没必要了
    //通知接口
    interface Subject_Delegate
    {
        void Notify();
        string SubjectState { get; set; }
    }
    //使用委托来解决在通知方法中的必须要遍历观察者情况(现在已经没有抽象观察者了)
    delegate void EventHandler();

    class Boss_delegate : Subject_Delegate
    {
        public event EventHandler Update;   //声明委托，名称叫Update
        private string action;

        public void Notify()
        {
            Update();   //访问通知方法时，调用更新方法
        }
        public string SubjectState
        {
            get { return action; }
            set { action = value; }
        }
    }

    #endregion
}
